package be.jabapage.snooker.service.competition.impl;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;

import org.apache.commons.lang.Validate;

import be.jabapage.snooker.exception.EntityNotFoundException;
import be.jabapage.snooker.exception.MultipleInstanceFoundException;
import be.jabapage.snooker.jdo.competition.Competition;
import be.jabapage.snooker.service.competition.api.ICompetitionService;


public class AppEngineCompetitionService implements ICompetitionService, Serializable {

	private static final long serialVersionUID = -3816718022896353756L;
	private final PersistenceManagerFactory pmf;

	public AppEngineCompetitionService(final PersistenceManagerFactory pmf) {
		Validate.notNull(pmf,"The persistence Manager factory cannot be null");
		this.pmf = pmf;
	}

	/**
	 * {@inheritDoc}
	 */
	public void store(final Competition competition) {
		Validate.notNull(competition);
		PersistenceManager pm = pmf.getPersistenceManager();
		try {
			pm.makePersistent(competition);
		} finally {
			pm.close();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public Competition retrieve(final Long key) throws EntityNotFoundException {
		Validate.notNull(key, "A key should be provided.");
		PersistenceManager pm = pmf.getPersistenceManager();
		try {
			Competition objectById = pm.getObjectById(Competition.class, key);
			if (objectById == null) {
				throw new EntityNotFoundException();
			}
			return objectById;
		} finally {
			pm.close();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public Competition retrieve(final String level) throws MultipleInstanceFoundException, EntityNotFoundException {
		Validate.notNull(level, "We cannot search any object without the level.");
		Set<Competition> resultSet = search(level);
		if (resultSet.size() == 0) {
			throw new EntityNotFoundException();
		}
		if (resultSet.size() > 1) {
			throw new MultipleInstanceFoundException(String.format("search criteria : (level = %s)",level));
		}
		Iterator<Competition> iterator = resultSet.iterator();
		return iterator.next();
	}

	/**
	 * {@inheritDoc}
	 */
	public void delete(final Long key) throws EntityNotFoundException {
		Validate.notNull(key, "We cannot delete an instance withour a key.");
		PersistenceManager pm = pmf.getPersistenceManager();
		try {
			Competition dataInstance = pm.getObjectById(Competition.class, key);
			if (dataInstance == null) {
				throw new EntityNotFoundException();
			}

			pm.deletePersistent(dataInstance);
		} finally {
			pm.close();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public Set<Competition> search(final String level) {
		Validate.notNull(level, "We need to have a level.  If you want to retrieve the entire list, use the wildcard for that.");
		PersistenceManager pm = pmf.getPersistenceManager();
		Query query = pm.newQuery(Competition.class);
		query.setFilter("level == levelParam");
		query.declareParameters("String levelParam");
		Set<Competition> resultSet = new HashSet<Competition>();

		try {
			@SuppressWarnings("unchecked")
			List<Competition> results = (List<Competition>) query.execute(level);
			if (results != null) {
				resultSet.addAll(results);
			}
		} finally {
			query.closeAll();
			pm.close();
		}
		return resultSet;
	}

	public Set<Competition> retrieveAllForRegion(final Long regionId) {
		Validate.notNull(regionId, "We need to have regionId.");
		PersistenceManager pm = pmf.getPersistenceManager();
		Query query = pm.newQuery(Competition.class);
		query.setFilter("regionId == regionParam");
		query.declareParameters("Long regionParam");
		Set<Competition> resultSet = new HashSet<Competition>();

		try {
			@SuppressWarnings("unchecked")
			List<Competition> results = (List<Competition>) query.execute(regionId);
			if (results != null) {
				resultSet.addAll(results);
			}
		} finally {
			query.closeAll();
			pm.close();
		}
		return resultSet;
	}
}
